from datetime import datetime, timedelta
from decimal import Decimal
from math import modf

class JNum(int):
    def __truediv__(self, other_frac):
        result = float(self.__value__) / float(other_frac)
        return JNum(int(result))
    def __init__(self, num):
        self.__value__ = num
    def __add__(self, other_frac):
        return JNum(self.__value__ + other_frac)
    def __radd__(self, other_frac):
        return JNum(self.__value__ + other_frac)
    def __mul__(self, other_frac):
        return JNum(self.__value__ * other_frac)
    def __rmul__(self, other_frac):
        return JNum(self.__value__ * other_frac)
    def __sub__(self, other_frac):
        return JNum(self.__value__ - other_frac)
    def __rsub__(self, other_frac):
        return JNum(other_frac - self.__value__)
    def __neg__(self):
        return JNum(-self.__value__)

def JulianDate(DateAndTime: datetime)-> Decimal:
    y, m, d = JNum(DateAndTime.year), JNum(DateAndTime.month), JNum(DateAndTime.day)
    J = 367 * y - (7 * (y + (m + 9) / 12)) / 4 - (3 * ((y + (m - 9) / 7) / 100 + 1 )) / 4 + 275 * m / 9 + d + 1721029
    h, m, s = Decimal(DateAndTime.hour), Decimal(DateAndTime.minute), Decimal(DateAndTime.second)
    J = Decimal(int(J))
    timedelta_in_seconds = (h * 3600 + m * 60 + s) - (12 * 3600)
    timedelta_in_one = timedelta_in_seconds / Decimal(3600 * 24)
    J += timedelta_in_one
    return J


def RJulianDate(J: Decimal)->datetime:
    JTime, JD = modf(J)
    JTime, JD = Decimal(JTime), JNum(JD)
    a = JD + 68569
    b = 4 * a / 146097
    c = a - (146097 * b + 3) / 4
    d = (4000 * (c + 1)) / 1461001
    e = c - (1461 * d) / 4 + 31
    f = (80 * e) / 2447
    day = e - (2447 * f) / 80
    g = f / 11
    month = f + 2 - 12 * g
    year = 100 * (b - 49) + d + g
    DateAndTime = datetime(year=year, month=month, day=day, hour=12, minute=0, second=0)
    JTime *= 24
    JTime, hours = modf(JTime)
    JTime *= 60
    JTime, minutes = modf(JTime)
    seconds = JTime * 60
    DateAndTime += timedelta(hours=hours, minutes=minutes, seconds=seconds)
    return DateAndTime